2023/04/05 更新: 為了避免本文章散落在不同網站,之後統一由部落格更新,再麻煩從部落格查看~
不以靜態繼承而是用動態組合的方式增加功能
UML 圖如下:
ProductDecorator 與 ProductA、ProductB 類別一樣都是以 Product interface 實作,並且擁有依照 Product 定義的成員,所以在呼叫operation()
時就可以呼叫成員的operation()
並且再新增額外的功能。
在 golang 中沒有繼承,所以在現有功能中新增功能其實就是利用 Decorator Pattern,但如果在 java 中,時常使用繼承來新增功能,但如果過度使用繼承,事實上沒有辦法重用程式,如下:
PS5 主機 interface 有PS5.StartGPUEngine()
來啟動顯示晶片,PS5WithCD{}
、PS5WithDigital{}
都會繼承此 interface 來實作,但如果這時候要在顯示晶片上新增加強功能,就需要在繼承一次導致PS5WithCDPlus{}
、PS5WithDigitalPlus{}
的產生,但看我們要做的事情的本質就是「一個」新增加強功能,需要「兩個」類是多餘的。
UML 圖如下:
相關的 code 在Github - go-design-patterns
code 如下:
package main
import "fmt"
type PS5 interface {
StartGPUEngine()
}
type PS5WithCD struct{}
func (p PS5WithCD) StartGPUEngine() {
fmt.Println("start normal gpu engine")
}
type PS5WithDigital struct{}
func (p PS5WithDigital) StartGPUEngine() {
fmt.Println("start normal gpu engine")
}
type PS5MachinePlus struct {
ps5Machine PS5
}
func (p *PS5MachinePlus) SetPS5Machine(ps5 PS5) {
p.ps5Machine = ps5
}
func (p PS5MachinePlus) StartGPUEngine() {
p.ps5Machine.StartGPUEngine()
fmt.Println("start plus plugin")
}
func main() {
ps5MachinePlus := PS5MachinePlus{
ps5Machine: PS5WithCD{}, // or PS5WithDigital{}
}
// ps5MachinePlus.SetPS5Machine(PS5WithDigital{}) // 可以在更換主機
ps5MachinePlus.StartGPUEngine()
}
我們將加強功能定義為一個 struct PS5MachinePlus{}
,這個PS5MachinePlus{}
介面跟PS5WithCD{}
、PS5WithDigital{}
介面都是以PS5
interface 來實作的,所以外部使用者呼叫的時候介面是沒有變更的。
PS5MachinePlus{}
擁有ps5Machine{}
,此成員依賴PS5
interface,讓 PS5WithCD{}
、PS5WithDigital{}
都可以帶入。
PS5MachinePlus{}
在內部實作StartGPUEngine()
時,會使用到ps5Machine{}
,ps5Machine{}
可以在創建 struct 的時候帶入,也可以透過 SetPS5Machine()
替換,他是動態帶入的,這也讓 Decorator Pattern 在 runtime 更加彈性。